#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define KEY_NUM 1
#define KEY0VALUE 0x01   //有效的按键值
#define INVAKEY 0xFF    //无效的按键值
#define IMX6ULKEY_NAME "key"
#define IMX6ULKEY_CNT 1 //设备号个数

struct irq_keydesc
{
    int gpio;
    int irqnum;
    unsigned char value;
    char name[10];
    irqreturn_t (*handler)(int, void *);
};

struct imx6ulkey_dev
{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;

    atomic_t keyvalue;   // 按键值
    atomic_t releasekey; // 是否释放按键
    struct timer_list timer;
    struct irq_keydesc irqkeydesc[KEY_NUM];
    unsigned char curkeynum; // 在irqkeydesc 数组中的当前下标

    wait_queue_head_t r_wait; // 读等待队列头
};

struct imx6ulkey_dev imx6ulkey;

//中断处理函数
static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct imx6ulkey_dev *dev = (struct imx6ulkey_dev *)dev_id;

    dev->curkeynum = 0;
    //dev->timer.data = (volatile long)dev_id;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));

    return IRQ_RETVAL(IRQ_HANDLED);
}

//定时处理函数
void timer_function(struct timer_list * t)
{
    struct imx6ulkey_dev *dev = from_timer(dev,t,timer);
    unsigned char num;
    unsigned char value;
    struct irq_keydesc *keydesc;

    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num];

    value = gpio_get_value(keydesc->gpio);
    if (value == 0) //按下了，没有松开。
    {
        atomic_set(&dev->keyvalue, keydesc->value);
    }
    else //已经松开按键 是一次完整的过程。
    {
        atomic_set(&dev->keyvalue, keydesc->value | 0x80);
        atomic_set(&dev->releasekey, 1);
    }

    if (atomic_read(&dev->releasekey))
    {
        wake_up_interruptible(&dev->r_wait);
    }
}

static int keyio_init(void)
{
    unsigned char i = 0;
    char name[10];
    int ret = 0;

    imx6ulkey.nd = of_find_node_by_path("/key"); // get the device node;
    if (imx6ulkey.nd == NULL)
    {
        printk("key node not found !\r\n");
        return -EINVAL;
    }

    for (i = 0; i < KEY_NUM; i++)
    {
        imx6ulkey.irqkeydesc[i].gpio = of_get_named_gpio(imx6ulkey.nd, "key-gpio", i);
        if (imx6ulkey.irqkeydesc[i].gpio < 0)
        {
            printk("can't get key%d\r\n", i);
        }
    }

    for (i = 0; i < KEY_NUM; i++)
    {
        memset(imx6ulkey.irqkeydesc[i].name, 0, sizeof(name));
        sprintf(imx6ulkey.irqkeydesc[i].name, "KEY%d", i);
        gpio_request(imx6ulkey.irqkeydesc[i].gpio, name);                           // 申请使用该gpio
        gpio_direction_input(imx6ulkey.irqkeydesc[i].gpio);                         // 设置输入
        imx6ulkey.irqkeydesc[i].irqnum = gpio_to_irq(imx6ulkey.irqkeydesc[i].gpio); //通过gpio 获得中断号
    }

    //申请中断
    imx6ulkey.irqkeydesc[0].value = KEY0VALUE;
    imx6ulkey.irqkeydesc[0].handler = key0_handler;

    for (i = 0; i < KEY_NUM; i++)
    {
        ret = request_irq(imx6ulkey.irqkeydesc[i].irqnum, imx6ulkey.irqkeydesc[i].handler,
                          IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, imx6ulkey.irqkeydesc[i].name, &imx6ulkey);
        if (ret < 0)
        {
            printk("irq %d request failed!\r\n", imx6ulkey.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    //初始化定时器
    //init_timer(imx6ulkey.timer);
    //imx6ulkey.timer.function = timer_function;
	timer_setup(&imx6ulkey.timer,timer_function,0);

    //初始化等待队列头
    init_waitqueue_head(&imx6ulkey.r_wait);
    return 0;
}



static int imx6ulkey_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6ulkey;
    return 0;
}

static ssize_t imx6ulkey_read(struct file *filp, char __user *buf, size_t cnf, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0;
    unsigned char releasekey = 0;
    struct imx6ulkey_dev *dev = (struct imx6ulkey_dev *)filp->private_data;

    if (filp->f_flags & O_NONBLOCK) //非堵塞访问
    {
        if (atomic_read(&dev->releasekey) == 0) //没有按键按下
            return -EAGAIN;                     // try again
    }
    else
    {
        ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
        if (ret < 0)
            goto wait_error;
    }

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey)
    {
        if (keyvalue & 0x80)
        { //最高位为1
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }
        else
        {
            goto data_error;
        }
    }
    else
    {
        goto data_error;
    }
	atomic_set(&dev->releasekey,0);
    return 0;

wait_error:
    return ret;
data_error:
    return -EINVAL;
}

unsigned int imx6ulkey_poll(struct file *filp, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    struct imx6ulkey_dev *dev = (struct imx6ulkey_dev *)filp->private_data;

    poll_wait(filp, &dev->r_wait, wait);

    if (atomic_read(&dev->releasekey))
    {
        mask = POLLIN | POLLRDNORM;
    }
    return mask;
}

static struct file_operations imx6ulkey_fops = {
    .owner = THIS_MODULE,
    .open = imx6ulkey_open,
    .read = imx6ulkey_read,
    .poll = imx6ulkey_poll,
};

static int key_probe(struct platform_device *dev)
{
    /* 1、构建设备号 */
    if (imx6ulkey.major)
    {
        imx6ulkey.devid = MKDEV(imx6ulkey.major, 0);
        register_chrdev_region(imx6ulkey.devid, IMX6ULKEY_CNT, IMX6ULKEY_NAME);
    }
    else
    {
        alloc_chrdev_region(&imx6ulkey.devid, 0, IMX6ULKEY_CNT, IMX6ULKEY_NAME);
        imx6ulkey.major = MAJOR(imx6ulkey.devid);
        imx6ulkey.minor = MINOR(imx6ulkey.devid);
    }

    /* 2、注册字符设备 */
    cdev_init(&imx6ulkey.cdev, &imx6ulkey_fops);
    cdev_add(&imx6ulkey.cdev, imx6ulkey.devid, IMX6ULKEY_CNT);

    /* 3、创建类 */
    imx6ulkey.class = class_create(THIS_MODULE, IMX6ULKEY_NAME);
    if (IS_ERR(imx6ulkey.class))
    {
        return PTR_ERR(imx6ulkey.class);
    }

    /* 4、创建设备 */
    imx6ulkey.device = device_create(imx6ulkey.class, NULL, imx6ulkey.devid, NULL, IMX6ULKEY_NAME);
    if (IS_ERR(imx6ulkey.device))
    {
        return PTR_ERR(imx6ulkey.device);
    }

    /* 5、初始化按键 */
    atomic_set(&imx6ulkey.keyvalue, INVAKEY);
    atomic_set(&imx6ulkey.releasekey, 0);
    keyio_init();
    return 0;
}

static int key_remove(struct platform_device *dev)
{
    unsigned int i = 0;

    //删除定时器
    del_timer_sync(&imx6ulkey.timer);

    //释放中断 与gpio
    for (i = 0; i < KEY_NUM; i++)
    {
        free_irq(imx6ulkey.irqkeydesc[i].irqnum, &imx6ulkey);
        gpio_free(imx6ulkey.irqkeydesc[i].gpio);
    }

    //删除cdev
    cdev_del(&imx6ulkey.cdev);
    //释放设备号
    unregister_chrdev_region(imx6ulkey.devid, imx6ulkey.devid);
    //删除设备
    device_destroy(imx6ulkey.class, imx6ulkey.devid);
    //删除类
    class_destroy(imx6ulkey.class);

    return 0;
}

static const struct of_device_id key_of_match[] = {
    {.compatible = "my-key"},
    {}};

static struct platform_driver key_driver = {
    .driver = {
        .name = "imx6ul-key",
        .of_match_table = key_of_match},
    .probe = key_probe,
    .remove = key_remove};

static int __init imx6ulkey_init(void)
{
    return platform_driver_register(&key_driver);
}

static void  __exit imx6ulkey_exit(void)
{
    return platform_driver_unregister(&key_driver);
}
module_init(imx6ulkey_init);
module_exit(imx6ulkey_exit);
MODULE_LICENSE("GPL");
